<template>
  <n-flex vertical class="container">
    <!-- <n-spin :show="loading.isShow" style="height: 100vh;"> -->
      <Header></Header>
      <n-collapse-transition :show="show" >
        <n-form
          :model="searchParams"
          label-placement="left"
          size="small"
          inline>
          <n-form-item
            label="开始时间"
            path="afterDate"
            >
            <n-date-picker
              v-model:value="searchParams.afterDate"
              :shortcuts="afterShortcuts"
              type="datetime"
              clearable />
          </n-form-item>
          <n-form-item
            label="结束时间"
            path="beforeDate"
            >
            <n-date-picker v-model:value="searchParams.beforeDate" type="datetime" clearable />
          </n-form-item>
          <n-form-item
            label=""
            path="onlyCurrent"
            >
            <n-checkbox v-model:checked="searchParams.onlyCurrent">
              只统计本人
            </n-checkbox>
          </n-form-item>
          <n-form-item>
            <n-button type="info" :loading="timelines.running" @click="runTasks()">开始统计</n-button>
          </n-form-item>
        </n-form>
      </n-collapse-transition>

    <!-- </n-spin> -->
    <n-flex justify="center" :align="'center'">
      <n-timeline :icon-size="22" size="large" style="width: 400px; margin-top: 40px;">
        <n-timeline-item
          v-for="(item, index) in timelines.rows"
          :type="item.state"
          :line-type="timelines.currentIndex - 1 < index ? 'dashed' : 'default'"
        >
          <template #icon v-if="item.state === 'info'" >
            <flicker :size="18" color="var(--n-icon-color)"></flicker>
          </template>
          <template #header>
            <b style="font-size: 18px; line-height: 26px;">{{ item.title }}</b>
          </template>
          <div v-if="timelines.currentIndex < index">&nbsp;</div>
          <div v-else>{{ item.content  || '&nbsp;' }}</div>
          <template #footer>
            <n-flex>
              <n-button
                v-show="item.result"
                type="success" size="small"
                @click="() => { $msg.warning('暂未实现') }">查看结果</n-button>
                <!-- && ["success" , "error" , "warning"].includes(item.state) -->
              <n-button
                v-show="item.state && ['success', 'error', 'warning'].includes(item.state)"
                :disabled="timelines.running"
                tertiary size="small" type="default"
                @click="runTasks(index)"
                >重试</n-button>
            </n-flex>
            </template>
        </n-timeline-item>
      </n-timeline>
    </n-flex>
  </n-flex>
</template>

<script setup lang="ts">
import Header from './component/Header.vue'
import { useConfigStore } from '@/store/config'
import { promiseAllWithProgress, retryWrapper } from '@/utils/promise'
import { CommitSchema, ProjectSchema, ExpandedCommitSchema, CommitStatsSchema } from '@gitbeaker/rest';
import type { TimelineItemProps } from 'naive-ui'
import { startOfYear, startOfMonth } from 'date-fns'
import { invoke } from '@tauri-apps/api/tauri'

import { ref } from 'vue'

onMounted(() => {
  // invoke('add_gitlab_info', { host: 'hello', token: '我的世界', isHttps: false }).then(res => {
  //   console.log(res)
  // }).catch(err => {
  //   console.error(err)
  // })
  const data = { host: '10.101.37.217', token: 'xMyjD_gnvAm9tDdraRMy', https: false };
  const loading = window.$message.loading('正确获取项目信息', { duration: 0 });
  invoke('add_gitlab_info', { data }).then(res => {
    console.log(res)
  }).catch(err => {
    window.$message.error(err)
  }).finally(() => {
    loading.destroy();
  })
})


const show = ref(true)

const $msg = window.$message

const searchParams = ref({
  onlyCurrent: true,
  afterDate: startOfYear(new Date()).getTime(),
  beforeDate: new Date().getTime(),
})

const afterShortcuts = {
  '当月': () => startOfMonth(new Date()).getTime(),
  '当年': () => startOfYear(new Date()).getTime(),
}

type TimelineAction<T> = (data: T) => Promise<Partial<Timeline<T>>> | Partial<Timeline<T>> | void

interface Timeline<T> {
  title: TimelineItemProps['title'],
  content?: string,
  state?: TimelineItemProps['type'],
  result?: T,
  action?: TimelineAction<T>
}

interface Timelines {
  running: boolean,
  currentIndex: number,
  rows: Timeline<any>[]
}

const getProjects = async () => {
  setCurrent({ content: '项目信息加载中。。。' })
  const projects = (await api.value.Projects.all({ perPage: '100' }))
    .filter(project => project.request_access_enabled);
  return {
    result: projects,
    content: `项目加载完成，共[${projects.length}]个项目`
  }
}

const getCommits = async (projects: ProjectSchema[]) => {
  setCurrent({ content: `项目提交记录加载中 [0 / ${projects.length}]`})
  const since = searchParams.value.afterDate ? new Date(searchParams.value.afterDate).toISOString() : undefined;
  let projectCommitsPromises = projects.map(async project => {
    const commits = (await api.value.Commits.all(project.id, { perPage: '100', since }))
      .map(item => ({
        author_name: item.author_name,
        committed_date: item.committed_date,
        id: item.id,
        short_id: item.short_id,
       }))
    return { ...project, commits }
  })
  let result;
  let commitsResult = await promiseAllWithProgress(projectCommitsPromises, (current) => {
    result = current
    const { successCount, errorCount, all } = result;
    setCurrent({ content: `项目提交记录加载中 [${successCount}${errorCount === 0 ? '': ' / ' + errorCount } / ${all}]` })
  })
  // 解析结果包装
  let commits = commitsResult.filter(item => item.success)
    .map(item => item.result)
  if (result) {
    const { successCount, errorCount, all } = result;
    return {
      content: `项目提交记录加载完成 [${successCount}${errorCount === 0 ? '': ' / ' + errorCount } / ${all}]`,
      result: commits,
      state: errorCount > 0 ? 'warning' : 'success'
    }
  }
}

interface ProjectCommits extends ProjectSchema {
  commits: (CommitSchema | ExpandedCommitSchema)[];
}

const filterCommits = (projects: ProjectCommits[]) => {
let innerProjects = projects;
  if (searchParams.value.onlyCurrent) {
    innerProjects = innerProjects.map(project => {
      return { ...project, commits: project.commits?.filter(commit => commit?.author_name === currentUser.value?.name) }
    })
  }
  if (searchParams.value.beforeDate) {
    const beforeDate = new Date(searchParams.value.beforeDate).toISOString()
    innerProjects = innerProjects.map(project => {
      return { id: project.id, commits: project.commits?.filter(commit => commit?.committed_date && commit.committed_date <= beforeDate) }
    })
  }
  let commitCount = 0;
  let result = innerProjects.filter(project => {
    const r = project.commits && project.commits.length > 0;
    if (r) {
      commitCount += project.commits.length;
    }
    return r;
   })
   return { result, content: `项目提交信息过滤完成，剩余[${result.length}]个项目，共[${commitCount}]条提交记录` }
}

const getCommitInfos = async (projects: ProjectCommits[]) => {
  const count = projects.flatMap(item => item.commits.length)
    .reduce((r, current) => r + current)
  setCurrent({ content: `提交记录详情加载中 [0 / ${count}]`})

  let commitCount = { successCount: 0, errorCount: 0 }
  await Promise.all(projects.map(async project => {
    const projectCommitsPromises = project.commits.map(async commit => await api.value.Commits.show(project.id, commit.short_id))
      .map(promise => retryWrapper(promise));
    await promiseAllWithProgress(projectCommitsPromises, ({ success, result, index }) => {
      if (success) {
        commitCount.successCount++;
        const data = {
          stats: result.stats
        }
        project.commits[index] = data as ExpandedCommitSchema;
      } else {
        commitCount.errorCount++;
      }
      setCurrent({ content: `提交记录详情加载中 [${commitCount.successCount}${commitCount.errorCount === 0 ? '': ' / ' + commitCount.errorCount } / ${count}]` })
    })
  }))
  return {
    content: `提交记录详情加载完成 [${commitCount.successCount}${commitCount.errorCount === 0 ? '': ' / ' + commitCount.errorCount } / ${count}]`,
    state: commitCount.errorCount > 0 ? 'warning' : 'success',
    result: projects
  }
}

const getCommitCount = (projects: ProjectCommits[]) => {
  const countArr = projects.flatMap(project =>
    project.commits.filter(commit => commit.stats)
      .map(commit => commit.stats as CommitStatsSchema));

  const data = countArr.reduce((result, current) => {
    return {
      additions: result.additions + current.additions,
      deletions: result.deletions + current.deletions,
      total: result.total + current.total,
    }
  })
  const { additions, deletions, total } = data;
  setCurrent({
    content: `添加[${additions}]行，删除[${deletions}]行，共[${total}]行`,
    result: data,
  })
}

const timelines = shallowRef<Timelines>({
  running: false,
  currentIndex: -1,
  rows: [
    {
      title: '加载项目',
      action: getProjects,
    },
    {
      title: '加载项目提交信息',
      action: getCommits,
    },
    {
      title: '过滤项目提交信息',
      action: filterCommits,
    },
    {
      title: '加载提交明细',
      action: getCommitInfos
    },
    {
      title: '提交行数统计',
      action: getCommitCount
    }
  ]
})

const configStore = useConfigStore()

const { getGitLabApi: api, currentUser } = toRefs(configStore)

const setCurrent = (timeline: Partial<Timeline<any>>) => {
  if (timeline) {
    timelines.value.rows[timelines.value.currentIndex] = { ...timelines.value.rows[timelines.value.currentIndex], ...timeline };
    triggerRef(timelines);
  }
}

const runTasks = async (startIndex = 0) => {
  timelines.value.running = true;
  timelines.value.currentIndex = startIndex;
  const length = timelines.value.rows.length;
  // 重置任务状态
  for (let i = startIndex; i < length; i++) {
    timelines.value.rows[i].state = 'default';
    timelines.value.rows[i].result = undefined;
    timelines.value.rows[i].content = undefined;
  }
  triggerRef(timelines);

  // 重跑任务
  while(timelines.value.currentIndex < length) {
    const data = timelines.value.currentIndex === 0 ? undefined : timelines.value.rows[timelines.value.currentIndex - 1].result;
    const action = timelines.value.rows[timelines.value.currentIndex]?.action;
    timelines.value.rows[timelines.value.currentIndex].state = 'info';
    // 调用函数处理异常
    try {
      const result = await action?.(data);
      setCurrent({ state: 'success', ...result });
    } catch (err) {
      console.error(err)
      setCurrent({ state: 'error', content: err instanceof Error ? err.message : err as string });
      break;
    }
    timelines.value.currentIndex++;
  }
  timelines.value.running = false;
}

</script>

<style lang="css" scoped>
.container {
  margin: 0 12px;
  height: 100vh;
}

</style>

<style lang="css">
@keyframes rotateImage {
  from {
    transform: rotate(0deg)
  }
  to {
    transform: rotate(360deg)
  }
}

.cicrle1{
  position: relative;
  min-width: 20px;
  min-height: 20px;
  border-radius: 50%;
  border: solid 2px red;
}

/* 设置动画后颜色 */
.cicrle1:before {
  background-color: rgba(0, 168, 253, 0.5);
}

.cicrle1:before,
.cicrle1:after{
    margin: 1px;
    content: '';
    width: 16px;
    height: 16px;
    position: absolute;
    left: 50%;
    top: 50%;
    border-radius: 50%;
    animation: warn 1s ease-out 0s infinite;
}

@keyframes warn {
  0% {
    transform: scale(0.4);
    opacity: 1;
  }

  25% {
    transform: scale(0.6);
    opacity: .8;
  }

  50% {
    transform: scale(0.8);
    opacity: .6;
  }

  75% {
    transform: scale(1);
    opacity: .4;
  }

  100% {
    transform: scale(1.2);
    opacity: 0;
  }
}

</style>
